package com.vois.jack.btmgr.devices.WLBtOpusDevice;

import android.os.Bundle;
import android.os.Message;

import com.vois.jack.btmgr.classicbase.BtDevCommonMsg;
import com.vois.jack.btmgr.classicbase.BtOpusCodedRecorder;
import com.vois.jack.btmgr.classicbase.BtRecorderInterface;
import com.vois.jack.btmgr.classicbase.BtScoRecorder;
import com.vois.jack.btmgr.devices.WLBtBtnDevice.WLBtBtnDevice;
import com.vois.jack.btmgr.util.ActionProcessUtil;
import com.vois.jack.btmgr.util.Logger;

public class WLBtOpusDevice extends WLBtBtnDevice {
    private final static int OPUS_SINGLE_PACK = 0x01;
    private final static int OPUS_COMPO_PACK = 0x03;
    private Logger logger = Logger.getLogger(WLBtOpusDevice.class);
    private BtOpusCodedRecorder opusCodedRecorder;
    private BtScoRecorder btScoRecorder;
    private byte[] mediaParam;
    private boolean isOpusSizeVariable;
    private int fixedOpusSize;
    private boolean usingOpusCoder;
    private int supportMediaStreamType;
    private ActionProcessUtil actionProcessUtil;
    private ResultCallback startRecorderCb;
    private ResultCallback stopRecorderCb;

    @Override
    protected int getMaxSppPacketDataSize() {
        return 10240;
    }

    @Override
    public void openDevice() {
        super.openDevice();

        actionProcessUtil = new ActionProcessUtil("BtOpus", getBtDevFsm().getHandler());
        actionProcessUtil.startActionExecution();
    }

    @Override
    public void closeDevice() {
        super.closeDevice();
        if (actionProcessUtil != null) {
            actionProcessUtil.stopActionExecution();
            actionProcessUtil = null;
        }
    }

    @Override
    public BtRecorderInterface getRecorder(int recorderType) {
        if (recorderType == OPUS_RECORDER) {
            opusCodedRecorder = new BtOpusCodedRecorder();
            return opusCodedRecorder;
        }else if (recorderType == SCO_RECORDER) {
            btScoRecorder = new BtScoRecorder();
            return btScoRecorder;
        }

        return null;
    }

    @Override
    public boolean startRecorder(ResultCallback callback) {
        startRecorderCb = callback;
        if (usingOpusCoder) {
            sendCommand(0x34, null, 0);
            return true;
        }

        return super.startRecorder(callback);
    }

    @Override
    public void stopRecorder(ResultCallback callback) {
        if (startRecorderCb != null) {
            startRecorderCb.onResult(false);
            startRecorderCb = null;
        }

        stopRecorderCb = callback;
        if (usingOpusCoder) {
            sendCommand(0x35, null, 0);
        }else {
            super.stopRecorder(callback);
        }
    }

    @Override
    public boolean isNeedHeartbeat() {
        return !usingOpusCoder;
    }

    //    private void debugInfo(String debugInfo) {
//        if (getListener() != null) {
//            Message message = Message.obtain();
//            message.what = BtDevCommonMsg.BT_DEV_DEBUG_INFO.getValue();
//            Bundle bundle = new Bundle();
//            bundle.putString(BtDevConstant.EXTRA_DEBUG_STR, debugInfo);
//            message.setData(bundle);
//            getListener().onMessage(this, message);
//        }
//    }

//    private String dumpArray(byte[] data, int datalen) {
//        int dataSize = datalen;
//        char[] out = new char[dataSize * 3];
//        char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
//        StringBuffer debugStr = new StringBuffer();
//        for (int i = 0, j = 0; i < dataSize; i++) {
//            out[j++] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4];
//            out[j++] = DIGITS_UPPER[0x0F & data[i]];
//            out[j++] = ',';
//        }
//
//        debugStr.append("-------------[" + dataSize + "]-----------------\n");
//        debugStr.append(new String(out));
//        debugStr.append("\n------------------------------------------\n");
//
//        return debugStr.toString();
//    }

    @Override
    protected boolean processCommand() {
        boolean ret = super.processCommand();

        if (ret) {
            return true;
        }

        switch (curCommand) {
            case 0x2B: {
                ret = true;
            }
            break;


            case 0x34: {
                // start record responce
                int result = curContentData[0] & 0xFF;

                if (startRecorderCb != null) {
                    startRecorderCb.onResult(result == 1);
                    startRecorderCb = null;
                }

                ret = true;
            }
            break;

            case 0x35: {
                // stop record responce
                int result = curContentData[0] & 0xFF;
                if (stopRecorderCb != null) {
                    stopRecorderCb.onResult(result == 1);
                    stopRecorderCb = null;
                }

                ret = true;
            }
            break;

            case 0xEE: {
                //audio incoming
                //logger.dumpArray(curContentData, curContentLen);
                if (opusCodedRecorder != null && curContentData != null) {
                    if (supportMediaStreamType == OPUS_COMPO_PACK) {
                        int currentProcessedLen = 0;
                        int opusSize = 0;
                        if (isOpusSizeVariable) {
                            while (currentProcessedLen < curContentLen) {
                                opusSize = (int) curContentData[currentProcessedLen];
                                currentProcessedLen++;
                                opusCodedRecorder.putEncodedRecordData(curContentData, currentProcessedLen, opusSize);
                                currentProcessedLen += opusSize;
                            }
                        } else if (curContentLen % fixedOpusSize == 0) {
                            while (currentProcessedLen < curContentLen) {
                                opusCodedRecorder.putEncodedRecordData(curContentData, currentProcessedLen, fixedOpusSize);
                                currentProcessedLen += fixedOpusSize;
                            }
                        }
                    }else if (supportMediaStreamType == OPUS_SINGLE_PACK) {
                        opusCodedRecorder.putEncodedRecordData(curContentData, 0, curContentLen);
                    }
                }

                ret = true;
            }
            break;

            case 0x1F: {
                mediaParam = new byte[curContentLen - 3];

                supportMediaStreamType = curContentData[0];

                if (supportMediaStreamType == OPUS_COMPO_PACK) {
                    System.arraycopy(curContentData, 1, mediaParam, 0, curContentLen - 3);
                    if (mediaParam[0] == 0x00) {
                        isOpusSizeVariable = false;
                        fixedOpusSize = (int) mediaParam[1];
                    } else {
                        isOpusSizeVariable = true;
                    }

                    usingOpusCoder = true;
                }else {
                    usingOpusCoder = false;
                }

                if (actionProcessUtil != null) {
                    actionProcessUtil.reportResult(this, null);
                }

                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_GET_MEDIA_FEATURE_RET.getValue();
                    getListener().onMessage(this, message);
                }
            }
            break;
        }

        return ret;
    }

    private void querySupportMediaAbility() {
        if (actionProcessUtil != null) {
            actionProcessUtil.addActionRequest(this, null, 300, new ActionProcessUtil.ActionCallback() {
                @Override
                public boolean onProcess(Object context, Bundle parameters) {
                    sendCommand(0x1F, null, 0);
                    return true;
                }

                @Override
                public void onResult(Object context, Bundle data) {

                }

                @Override
                public void onError(Object context, int errorCode) {

                }

                @Override
                public void onTimeout(Object context) {
                    querySupportMediaAbility();
                }
            });
        }
    }

    @Override
    protected void onSppSocketConnected() {
        super.onSppSocketConnected();
        querySupportMediaAbility();
    }

    @Override
    public boolean useOwnPlayTone() {
        return usingOpusCoder;
    }

    @Override
    public int getDefaultRecorderType() {
        return usingOpusCoder ? OPUS_RECORDER: SCO_RECORDER;
    }
}
